//
// Created by martin on 4/2/22.
//

#ifndef MUDUO_FTPDATASERVER_H
#define MUDUO_FTPDATASERVER_H

#include "muduo/base/noncopyable.h"
#include "muduo/net/EventLoop.h"
#include "muduo/net/TcpServer.h"
#include "muduo/net/InetAddress.h"
#include "muduo/net/TcpConnection.h"

#include <memory>

namespace ftp
{

class DataAcceptor;

/**
 * @brief Ftp Data Server for data connection.
 *
 * One Ftp Data Server per data connection.
 *
 * Warning: Default data port is 20 for PORT mode, not PASV mode.
 *
 * Not thread safe. . Must be called in owner IO Loop.
 *
 * @note Used for PASV mode, and it needs to specific different data port
 * which different FtpDataServer listen on at the same time.
 */
class FtpDataServer : muduo::noncopyable, public std::enable_shared_from_this<FtpDataServer>
{
public:
	FtpDataServer(muduo::net::EventLoop* loop,
		const muduo::net::InetAddress& serverAddr,
		const std::string& name = "FtpDataServer",
		muduo::net::TcpServer::Option option = muduo::net::TcpServer::kNoReusePort);

	~FtpDataServer();

	//---------------------------------------------------------
	// Control Ftp Data Server
	//---------------------------------------------------------

	void start();

	void setDataPortRange(uint16_t minPort, uint16_t maxPort);

	void shutdown();

	void disconnect();

	//---------------------------------------------------------
	// Status of Ftp Data Server
	//---------------------------------------------------------

	bool listening() const;

	muduo::net::InetAddress listenAddress() const;

	//---------------------------------------------------------
	// Set Callbacks
	//---------------------------------------------------------

	void setConnectionCallback(muduo::net::ConnectionCallback cb)
	{
		connectionCallback_ = std::move(cb);
	}

	void setMessageCallback(muduo::net::MessageCallback cb)
	{
		messageCallback_ = std::move(cb);
	}

	void setWriteCompleteCallback(muduo::net::WriteCompleteCallback cb)
	{
		writeCompleteCallback_ = std::move(cb);
	}

private:

	void newConnection(int sockfd, const muduo::net::InetAddress& peerAddr);
	void removeConnection(const muduo::net::TcpConnectionPtr& conn);
	void removeConnectionInLoop(const muduo::net::TcpConnectionPtr& conn);

	void onConnection(const muduo::net::TcpConnectionPtr& conn);

	void onMessage(const muduo::net::TcpConnectionPtr& conn,
		muduo::net::Buffer* buffer,
		muduo::Timestamp receiveTime);

	void onWriteComplete(const muduo::net::TcpConnectionPtr& conn);

	void profile();

	muduo::net::EventLoop* loop_;                // owner EventLoop, IO loop
	const std::string ipPort_;                   // string of ip, port
	const std::string name_;                     // base name of FtpDataServer
	std::unique_ptr<DataAcceptor> acceptor_;     // Acceptor for Data Connection
	bool listened_;	                             // flag whether FtpDataServer is listening on port
	muduo::net::InetAddress serverAddr_;
	static muduo::AtomicInt64 nextConnId_;       // connection id
	muduo::net::ConnectionCallback connectionCallback_;
	muduo::net::MessageCallback messageCallback_;
	muduo::net::WriteCompleteCallback writeCompleteCallback_;

	muduo::net::TcpConnectionPtr connection_;    // TcpConnection

	// Data port range
	bool autoDataPortFlag_;
	int minDataPort_;
	int maxDataPort_;

	static constexpr uint16_t kDefatulMinPort = 1024;
	static constexpr uint16_t kDefatulMaxPort = 65535;
};

typedef std::shared_ptr<FtpDataServer> FtpDataServerPtr;

} // namespace ftp

#endif //MUDUO_FTPDATASERVER_H
